package cn.bearspark.happening.provider.service.impl;

import cn.bearspark.common.util.ConvertBeanUtil;
import cn.bearspark.common.util.ConvertInfoUtil;
import cn.bearspark.file.rpc.FileRpc;
import cn.bearspark.happening.dto.happening.VoteInfo4Publish;
import cn.bearspark.happening.dto.vote.*;
import cn.bearspark.happening.enumeration.DeadEnum;
import cn.bearspark.happening.enumeration.vote.AnonymousEnum;
import cn.bearspark.happening.enumeration.vote.SelectedEnum;
import cn.bearspark.happening.enumeration.vote.VoteTypeEnum;
import cn.bearspark.happening.enumeration.vote.VotedEnum;
import cn.bearspark.happening.provider.dao.buffer.VoteHashBuffer;
import cn.bearspark.happening.provider.dao.buffer.VoteOptionBiHashBuffer;
import cn.bearspark.happening.provider.dao.buffer.VoteOptionIdListBuffer;
import cn.bearspark.happening.provider.dao.buffer.VoteUserIdSetBuffer;
import cn.bearspark.happening.provider.dao.co.VoteCO;
import cn.bearspark.happening.provider.dao.co.VoteOptionCO;
import cn.bearspark.happening.provider.dao.mapper.VoteMapper;
import cn.bearspark.happening.provider.dao.mapper.VoteOptionMapper;
import cn.bearspark.happening.provider.dao.mapper.VoteUserMapper;
import cn.bearspark.happening.provider.dao.po.VotePO;
import cn.bearspark.happening.provider.service.VoteService;
import cn.bearspark.happening.provider.service.bo.VoteSimpleBO;
import cn.bearspark.user.dto.UserSlimDTO;
import cn.bearspark.user.rpc.UserBaseRpc;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author f
 */
@Service
public class VoteServiceImpl implements VoteService {

    /**
     * 选项内容的前缀
     */
    private static final String OPTION_CONTENT_PREFIX = "\"";

    /**
     * 选项内容的后缀
     */
    private static final String OPTION_CONTENT_SUFFIX = "\"";

    /**
     * 选项内容间的间隔符
     */
    private static final String OPTION_CONTENT_DELIMITER = "、";

    @DubboReference
    private UserBaseRpc userBaseRpc;
    @DubboReference
    private FileRpc fileRpc;

    private final VoteMapper voteMapper;
    private final VoteOptionMapper voteOptionMapper;
    private final VoteUserMapper voteUserMapper;

    private final VoteHashBuffer voteBuffer;
    private final VoteUserIdSetBuffer voteUserIdSetBuffer;
    private final VoteOptionBiHashBuffer voteOptionBiHashBuffer;
    private final VoteOptionIdListBuffer voteOptionIdListBuffer;

    public VoteServiceImpl(VoteMapper voteMapper,
                           VoteOptionMapper voteOptionMapper,
                           VoteUserMapper voteUserMapper,
                           VoteHashBuffer voteBuffer,
                           VoteUserIdSetBuffer voteUserIdSetBuffer,
                           VoteOptionBiHashBuffer voteOptionBiHashBuffer,
                           VoteOptionIdListBuffer voteOptionIdListBuffer) {
        this.voteMapper = voteMapper;
        this.voteOptionMapper = voteOptionMapper;
        this.voteUserMapper = voteUserMapper;
        this.voteBuffer = voteBuffer;
        this.voteUserIdSetBuffer = voteUserIdSetBuffer;
        this.voteOptionBiHashBuffer = voteOptionBiHashBuffer;
        this.voteOptionIdListBuffer = voteOptionIdListBuffer;
    }

    @Override
    public void publish(VoteInfo4Publish voteInfo, long voteId, long publisherId) {
        // 保存投票基础数据
        VotePO votePO = ConvertBeanUtil.convert(voteInfo, VotePO.class);
        votePO.setVoteId(voteId);
        votePO.setPublisherId(publisherId);
        voteMapper.insert(votePO);

        // 阻止删除图片投票的图片路径
        int voteType = voteInfo.getVoteType();
        List<Option4Publish> optionList = voteInfo.getOptionList();
        if (voteType == VoteTypeEnum.PHOTO_VOTE.getCode()) {
            fileRpc.preventDeletion(optionList.stream().map(Option4Publish::getOptionPhotoUrl).toList());
        }

        // 保存投票选项数据
        voteOptionMapper.insertBatch(voteId, optionList, voteType);
    }

    @Override
    public String vote(VoteReqDTO voteReqDTO) {
        long voteId = voteReqDTO.getVoteId();
        long voterId = voteReqDTO.getVoterId();
        List<Integer> optionIdList = voteReqDTO.getOptionIdList();

        // 增加投票的投票人数
        voteMapper.incrVoteNum(voteId);

        // 增加选项的选中数
        voteOptionMapper.incrSelectNum(voteId, optionIdList);

        // 如果是匿名投票，则无需显示具体向什么选项投票
        int anonymous = voteReqDTO.getAnonymous();
        voteUserMapper.insertBatch(voteId, voterId, optionIdList, anonymous);

        // 清理投票的热数据
        voteBuffer.cleanHot(voteId);

        // 清理投票选项的热数据
        voteOptionBiHashBuffer.cleanBatchHot(voteId, optionIdList);

        // 如果不是匿名投票，则需要将投票记录到表中，并且显示具体向什么选项投票
        return anonymous == AnonymousEnum.PUBLIC.getCode() ?
                getOptionsInfo(voteId, optionIdList) :
                null;
    }

    // 获取指定投票中指定投票选项的投票信息
    private String getOptionsInfo(long voteId, List<Integer> optionIdList) {
        Map<Integer, String> optionContentMap = !CollectionUtils.isEmpty(optionIdList) ?
                voteOptionBiHashBuffer.mapOptionIdContent(voteId, optionIdList) :
                new HashMap<>();
        return optionIdList.stream()
                .map(optionId -> OPTION_CONTENT_PREFIX + optionContentMap.get(optionId) + OPTION_CONTENT_SUFFIX)
                .collect(Collectors.joining(OPTION_CONTENT_DELIMITER));
    }

    @Override
    public VoteDetailDTO detail(long voteId, long userId) {
        // 查询投票的基本信息
        VoteDetailDTO voteDetailDTO = getVoteBase(voteId);

        // 查询投票的发布者的用户信息
        setPublisherInfo(voteDetailDTO);

        // 查询投票的选项信息
        List<Option4Display> optionList = getOptionList(voteId);
        voteDetailDTO.setOptionList(optionList);

        // 为不同用户设置投票选项的不同信息
        if (voteDetailDTO.getUserId() != userId) {
            setVoteInfo4OtherUser(voteId, userId, optionList, voteDetailDTO);
        } else {
            setVoteInfo4Publisher(voteId, userId, optionList, voteDetailDTO);
        }

        return voteDetailDTO;
    }

    // 针对发布者设置投票信息
    private void setVoteInfo4Publisher(long voteId, long userId, List<Option4Display> optionList,
                                       VoteDetailDTO voteDetailDTO) {
        // 如果查询投票详情的是发布者，则
        // 1. 将所有选项设置为未选中，并设置未投票
        optionList.forEach(option4Display -> option4Display.setSelected(SelectedEnum.UNSELECTED.getCode()));
        voteDetailDTO.setVoted(VotedEnum.UN_VOTED.getCode());

        // 2. 查询其关注者的投票信息
        List<UserSlimDTO> followerSlimInfoList = userBaseRpc.listFSlimInfo(userId);
        List<Long> followerIdList = followerSlimInfoList.stream().map(UserSlimDTO::getUserId).toList();
        Map<Long, Set<Integer>> voteUserIdMap = voteUserIdSetBuffer.mapVoteOptionIdSet(voteId, followerIdList);
        List<Integer> optionIdList = voteUserIdMap.values().stream().flatMap(Collection::stream).distinct().toList();

        // key：选项的 id，value：选项的内容
        Map<Integer, String> optionContentMap = !CollectionUtils.isEmpty(optionIdList) ?
                voteOptionBiHashBuffer.mapOptionIdContent(voteId, optionIdList) :
                new HashMap<>();

        // key：投票者的用户 id，value：投票信息
        Map<Long, String> optionInfoMap = voteUserIdMap.entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> entry.getValue().stream().map(optionId ->
                        OPTION_CONTENT_PREFIX + optionContentMap.get(optionId) + OPTION_CONTENT_SUFFIX
                ).collect(Collectors.joining(OPTION_CONTENT_DELIMITER))
        ));

        voteDetailDTO.setVoterInfoList(followerSlimInfoList.stream().map(userSlimDTO -> {
            VoterInfo voterInfo = ConvertBeanUtil.convert(userSlimDTO, VoterInfo.class);
            voterInfo.setOptionsInfo(optionInfoMap.get(userSlimDTO.getUserId()));
            return voterInfo;
        }).filter(voterInfo -> StringUtils.hasText(voterInfo.getOptionsInfo())).toList());
    }

    // 针对其它用户设置投票信息
    private void setVoteInfo4OtherUser(long voteId, long userId, List<Option4Display> optionList,
                                       VoteDetailDTO voteDetailDTO) {
        // 如果查询投票详情的不是发布者，则查询该用户是否投票
        Set<Integer> optionIdSet = voteUserIdSetBuffer.getVoteOptionIdSet(voteId, userId);
        optionList.forEach(option -> option.setSelected(optionIdSet.contains(option.getOptionId()) ?
                SelectedEnum.SELECTED.getCode() :
                SelectedEnum.UNSELECTED.getCode()
        ));
        voteDetailDTO.setVoted(optionIdSet.isEmpty() ?
                VotedEnum.UN_VOTED.getCode() :
                VotedEnum.VOTED.getCode()
        );
    }

    // 获取投票的选项列表
    private List<Option4Display> getOptionList(long voteId) {
        long voteTotalNum = voteUserMapper.countVoteTotalNum(voteId);
        List<Integer> optionIdList = voteOptionIdListBuffer.listVoteOptionId(voteId);
        Map<Integer, VoteOptionCO> voteOptionCOMap = voteOptionBiHashBuffer.mapVoteOption(voteId, optionIdList);
        return optionIdList.stream().map(optionId -> {
            VoteOptionCO voteOptionCO = voteOptionCOMap.get(optionId);
            Option4Display option4Display = ConvertBeanUtil.convert(voteOptionCO, Option4Display.class);
            option4Display.setOptionPercent(voteTotalNum != 0 ?
                    String.format("%.0f", voteOptionCO.getSelectNum() * 100.0 / voteTotalNum) :
                    "0");
            return option4Display;
        }).toList();
    }

    // 查询投票的基本信息
    private VoteDetailDTO getVoteBase(long voteId) {
        VoteCO voteCO = voteBuffer.get(voteId);
        VoteDetailDTO voteDetailDTO = ConvertInfoUtil.convert(voteCO, VoteDetailDTO.class);
        voteDetailDTO.setUserId(voteCO.getPublisherId());
        voteDetailDTO.setDead(LocalDateTime.now().isBefore(voteCO.getDeadline()) ?
                DeadEnum.ALIVE.getCode() :
                DeadEnum.DEAD.getCode()
        );
        return voteDetailDTO;
    }

    // 获取投票发布者的信息，将其填充到指定的投票详情对象中
    private void setPublisherInfo(VoteDetailDTO voteDetailDTO) {
        UserSlimDTO publisherSlimDTO = userBaseRpc.getSlimInfo(voteDetailDTO.getUserId());
        voteDetailDTO.setUsername(publisherSlimDTO.getUsername());
        voteDetailDTO.setAvatarUrl(publisherSlimDTO.getAvatarUrl());
    }

    @Override
    public Map<Long, VoteSimpleBO> mapVoteSimpleInfo(List<Long> voteIdList) {
        return voteBuffer.mapSimple(voteIdList).entrySet().stream().collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> ConvertBeanUtil.convert(entry.getValue(), VoteSimpleBO.class)
        ));
    }

    @Override
    public VoteSimpleBO getVoteSimpleInfo(Long voteId) {
        if (voteId == null) {
            return null;
        }
        return ConvertBeanUtil.convert(voteBuffer.getSimple(voteId), VoteSimpleBO.class);
    }

    @Override
    public void delete(long voteId) {
        // 删除 投票的基础数据
        voteMapper.delete(voteId);

        // 删除 图片投票的图片数据
        List<String> photoUrlList = voteOptionMapper.listPhotoUrlByVoteId(voteId);
        if (!photoUrlList.isEmpty()) {
            fileRpc.deleteBatch(photoUrlList);
        }

        // 删除 投票的选项数据
        voteOptionMapper.deleteByVoteId(voteId);

        // 清理投票的缓存
        voteBuffer.clean(voteId);

        // 清理投票选项的缓存
        voteOptionBiHashBuffer.cleanBatch(voteId);
        voteOptionIdListBuffer.clean(voteId);

        // 清理投票用户的缓存
        voteUserIdSetBuffer.clean(voteId);
    }
}
